package com.hax2max.control;

import java.util.ArrayList;

import android.content.Context;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;

import com.hax2max.control.FriendStalkerException.IExternalClient;
import com.hax2max.model.ExternalData;
import com.hax2max.model.FootPrint;
import com.hax2max.model.Profile;

public class DataHandler{

	public static Profile ownprofile;
	
	private static final String TAG = "DataHandler";
	
	private static DataHandler instance;
	
	private static ProfileFactory pf;
	
	private Context ctx;
	
	
	/**
	 * Creates a DataHandler for handling data flow between view and control layers.
	 * Needs the Context to operate. [getContext()]
	 * @param contx
	 */
/*	public DataHandler(Context contx) {
		ctx = contx;
		init();
	}*/
	
	private DataHandler(Context contx) {
		ctx = contx;
		init();
	}
	
	/**
	 * Get instance of datahandler or create it...
	 * 
	 * @param contx
	 * @return
	 */
	public static DataHandler getInstance(Context contx) {
		if(instance == null)
			instance = new DataHandler(contx);
		return instance;
	}
	
	/**
	 * Veaker version of datahandler, witch returns null if not created...
	 * 
	 * @return
	 */
	public static DataHandler getInstance() {
		return instance;
	}
	
	private void init() {
		pf = new ProfileFactory(ctx);
	}
	
	/**
	 * Checks if we got access to any kind of network, mobile or wi-fi to transfer data.
	 * @return true if we got access else false
	 */
	private boolean internetConnectionAvailable() { 
		if(ctx == null ) return false;
		ConnectivityManager cm = (ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo nwInfo = cm.getActiveNetworkInfo();
		boolean available = nwInfo.isConnected();
		Log.e(TAG,"Checking for Internet Connection, Available: " + available);
		return available;
	}
	
	/**
	 *	Returns a Cursor that holds information about the friends in the friendlist
	 *	Retruns null if user not logged in
	 *	@return Cursor 
	 * 
	 */
	public Cursor getFriendsListCursor() {
		if(isLoggedIn()) {
			return pf.getFriendsCursorLocal(ownprofile);			
		}
		return null;
	}
	
	public Cursor getFriendsListCursorAllData(String[] columns){
		if(isLoggedIn())
			return pf.getFriendsCursorAllDataLocal(columns);
		else 
			return null;
	}
	
	public Cursor getProfileCursor(String username) {
		if(isLoggedIn()) {
			return pf.getProfileCursor(username);			
		}
		return null;
	}
	
	
	/**
	 * Gets a Friendlist as a ArrayList with Profile objects
	 * Return null if user not logged in
	 * 
	 */
	public ArrayList<Profile> getFriendsList() {
		if(isLoggedIn()) {
			return pf.getFriendsLocal(ownprofile);			
		}
		return null;
	}
	/**
	 * Updates the friendlist from external servers
	 * Saving changes to database.
	 * @throws IExternalClient 
	 */
	public void updateFriendsList() throws IExternalClient {
		if(internetConnectionAvailable()){
			pf.getFriendsExternal(ownprofile);
			Log.e(TAG,"Friends updated external.");
			//notify
		}
	}
	
	/**
	 * Takes a profile and saves it to the database, preferable the ownprofile edited.
	 * 
	 * @param profile
	 */
	public void updateOwnProfile(Profile profile) {
		if(isLoggedIn()){
			profile.password = ownprofile.password;
			pf.saveProfile(profile);
		}
	}	
	
	
	/**
	 * Login the user on the system, can login both internal and external.
	 * If the login works i.e. username and passowrd mathc it returns that user's profile.
	 * Else it throws an UserNotValidException
	 * 
	 * @param username
	 * @param password
	 * @return profile
	 * @throws FriendStalkerException 
	 */
	public Profile loginUser(String username, String password) throws FriendStalkerException {	
		ownprofile = pf.loginUser(username, password,internetConnectionAvailable());
		
		new Thread(new Runnable() { //starting thread
			  public void run() {
				  	// 	login the external clients...					
					pf.loginExternalClient(ownprofile.externalData);
					//update list from server
					pf.getFriendsExternal(ownprofile);
					
			  }
			}).start();

		return ownprofile;
	}
	
	/**
	 * Logs the user out of the system
	 */
	public void logOut() {
		pf.logOut();
	}
	/**
	 * Checks if the user is logged in
	 * @return true if logged in else false
	 */
	public boolean isLoggedIn() {
		return (ownprofile != null);
	}
	/**
	 *  Log the user out of the system. Clears the ownprofile and the friendlist
	 */
	public void logout() {
		ownprofile = null;
		
	}
	
	
	/**
	 * Save the user's actualfootprint
	 * @param fp
	 */
	public void saveFootPrint(FootPrint fp ) {
		if (ownprofile!=null)
		{
			if(ownprofile.statuses != null) {
				fp.statusId = ownprofile.statuses[0].statusId;
			}else {
				fp.statusId = 1;
			}
			pf.saveFootPrint(fp, ownprofile.username);
		}
	}
	
	/**
	 * Sends an status update to a external system
	 * Needs the system's application Id and the new status text
	 * if the user dont have the system false is returned and nothing is send
	 * @param applicationId
	 * @param statusText
	 * @return true if send was ok
	 * @throws IExternalClient 
	 */
	public boolean sendStatus(int applicationId, String statusText) throws IExternalClient {
		for(ExternalData exd : ownprofile.externalData) {
			if( applicationId == exd.application) {
				exd.statusText = statusText;
				return CommunicationConnector.getInstance().updateStatus(exd);
			}
		}
		return false;
	}	
	/**
	 * returns a list of all external data objects
	 * @return
	 */
	public ArrayList<ExternalData> getAllExternalData()
	{
		
		return pf.getAllExternalData();
	}
	
	public ExternalData getExternalData(Profile profile, int applicationID, boolean returnEmpty)
	{
	return 	pf.getExternalData(profile, applicationID, returnEmpty);
	}
	/**
	 * gets the latest status from external data sources
	 * overwrite the status field in ExternalData object
	 * @param ed
	 * @return
	 * @throws IExternalClient
	 */
	public ExternalData updateStatus(ExternalData ed) throws IExternalClient
	{
		return pf.getStatus(ed);
	}
	/**
	 * gets latest statuses from external data sources
	 * overwrite the status field in ExternalData object
	 * @param edList
	 * @return
	 * @throws FriendStalkerException
	 */
	public ExternalData[] updateStatus(ArrayList<ExternalData> edList) throws FriendStalkerException
	{
		return pf.getStatus(edList);
	}
	/**
	 * saves external data object to local database
	 * @param ed
	 * @throws IExternalClient 
	 */
	public void saveExternalData(ExternalData ed) throws IExternalClient
	{
		pf.saveExternalData(ed, internetConnectionAvailable());
	
	}
	/**
	 * saves external data objects to local database
	 * @param ed
	 */
	public void saveExternalData(ExternalData[] edList)
	{
		pf.saveExternalData(edList);
	}
	
	public void changeLoginExternalClient(int appId, String username, String password) throws IExternalClient
	{
		pf.changeLoginExternalClient(appId, username, password);
	}
	
	public Object executeMethodExternalClient(int appId, String method, String ...parameters) throws FriendStalkerException.IExternalClient
	{
		return pf.executeMethodExternalClient(appId, method, parameters);
	}
}